package com.jiangyg.mall.db.datasource;

import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * 类描述：动态数据源切面类
 * 
 * @author jiangyg
 * @date 2020-10-17
 * @version 2.0
 */
@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {

	/**
	 * 日志
	 */
	private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);

	/**
	 * 功能描述：表示匹配带有自定义注解的方法
	 */
	@Pointcut("@annotation(com.jiangyg.mall.db.datasource.Dynamic)")
	public void pointcut() {

	}

	@Before("pointcut()")
	public void before(JoinPoint point) {
		// 获取执行方法
		MethodSignature signature = (MethodSignature) point.getSignature();
		// 目标类和方法
		Class<?> clazz = getTargetClass(point.getTarget());
		Method method = getTargetMethod(signature.getMethod(), clazz);
		// 获取方法注解：先从方法上获取注解，如果没有再从类上获取
		Dynamic dynamic = method.getAnnotation(Dynamic.class);
		if (dynamic == null) {
			dynamic = clazz.getAnnotation(Dynamic.class);
		}
		if (dynamic == null) {
			throw new IllegalArgumentException("当前类[" + clazz + "]方法[{" + method.getName() + "}]获取@Dynamic注解失败！");
		}
		// 切换数据源
		String datasource = dynamic.value();
		DataSourceContextHolder.set(datasource);
		if (logger.isDebugEnabled()) {
			logger.debug("当前线程[{}]类[{}]方法[{}]已切换数据源[{}]", Thread.currentThread().getName(), clazz, method.getName(), datasource);
		}
	}

	@After("pointcut()")
	public void after(JoinPoint point) {
		if (logger.isDebugEnabled()) {
			// 获取执行方法
			MethodSignature signature = (MethodSignature) point.getSignature();
			Method method = signature.getMethod();
			// 目标类
			Class<?> clazz = getTargetClass(point.getTarget());
			final String ds = DataSourceContextHolder.current();
			logger.debug("当前线程[{}]类[{}]方法[{}]已清空数据源[{}]", Thread.currentThread().getName(), clazz, method.getName(), ds);
		}
		DataSourceContextHolder.clean();
	}

	/**
	 * 功能描述：获取最终的执行方法
	 * 
	 * @author jiangyg
	 * @date 2021-03-26
	 * @param method 接口方法
	 * @param clazz  最终执行类
	 * @return 执行方法
	 */
	protected Method getTargetMethod(Method method, Class<?> clazz) {
		if (method.getDeclaringClass() == clazz) {
			return method;
		}
		// 获取目标类所有公共方法
		final Method[] methods = clazz.getMethods();
		for (Method m : methods) {
			// 比较方法名
			if (!StringUtils.equals(m.getName(), method.getName())) {
				continue;
			}
			// 先比较参数个数
			Type[] types1 = m.getGenericParameterTypes();
			Type[] types2 = method.getGenericParameterTypes();
			if (types1.length != types2.length) {
				continue;
			}
			// 然后再比较类型
			boolean f = true;
			for (int i = 0; i < types1.length; i++) {
				if (types1[i].getClass() != types2[i].getClass()) {
					f = false;
					break;
				}
			}
			if (f) {
				return m;
			}
		}
		return method;
	}

	/**
	 * 功能描述：获取最终的目标class
	 * 
	 * @param target 目标类
	 * @return 类
	 */
	protected Class<?> getTargetClass(Object target) {
		Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
		if (targetClass == null && target != null) {
			targetClass = target.getClass();
		}
		return targetClass;
	}

}
